package com.ayz.gateway.springbootstarter.processor;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;

import com.ayz.gateway.common.constants.ApiData;
import com.ayz.gateway.common.constants.MethodData;
import com.ayz.gateway.sdk.app.cache.ApiDataHolder;
import com.ayz.gateway.springbootstarter.annotation.ApiProviderClazz;
import com.ayz.gateway.springbootstarter.annotation.ApiProviderMethod;
import com.ayz.gateway.springbootstarter.config.GatewayClientProperties;
import com.ayz.gateway.springbootstarter.exception.ApiProviderMethodNotSupportException;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName GatewaySDKApplication
 * @Description TODO: 在每个Bean初始化即将注入容器前，检查类上是否有 @ApiProviderClazz
 *                    或者方法上是否有@ApiProviderMethod
 * 约定：类上必须带有 @ApiProviderClazz代表当前类需要被API网关管理
 *      方法上必须有 @ApiProviderMethod 描述当前方法的http映射信息
 * @Author 快乐的星球
 * @Date 2023/6/3 15:05
 * @Version 1.0
 **/
@Getter
@Setter
public class GatewaySDKApplication implements BeanPostProcessor {
    /**
     * GatewaySDK持有应用程序所有需要上报的API
     */
    private ApiDataHolder apiDataHolder;
    /**
     * Spring boot starter需要的配置参数
     */
    private GatewayClientProperties clientProperties;

    private static final String HTTP_URI_SEPARATOR="/";

    public GatewaySDKApplication(ApiDataHolder apiDataHolder, GatewayClientProperties clientProperties) {
        this.apiDataHolder = apiDataHolder;
        this.clientProperties = clientProperties;
    }

    public GatewaySDKApplication() {
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class<?> beanClass = bean.getClass();
        //1: 检查当前类是否被@ApiProviderClazz注解
        ApiProviderClazz apiProviderClazz = beanClass.getAnnotation(ApiProviderClazz.class);
        if(apiProviderClazz!=null){
            String interfaceName = apiProviderClazz.interfaceName();
            boolean isAsync = apiProviderClazz.isAsync();
            long timeout = apiProviderClazz.timeout();

            if(StrUtil.isBlank(interfaceName)){
                interfaceName=beanClass.getInterfaces()[0].getName();
            }
            ApiData apiData=new ApiData();
            apiData.setApplicationName(clientProperties.getApplicationName());
            apiData.setInterfaceName(interfaceName);
            apiData.setRpcRegistryAddress(clientProperties.getRegistryAddress());
            apiData.setTimeout(timeout);
            apiData.setAsync(isAsync);
            /**
             * 2.检查当前类的哪些方法被@ApiProviderMethod注解
             */
            List<MethodData> methodDataList = parseMethodAnnotation(beanClass);
            apiData.setMethodDataList(methodDataList);

            this.apiDataHolder.addApiData(interfaceName,apiData);
        }
        return bean;
    }

    /**
     * 解析方法上包含的@ApiProviderMethod注解
     * 注意:方法的修饰符必须是public 否则会失效
     * @param beanClass
     * @return
     */
    private List<MethodData> parseMethodAnnotation(Class<?> beanClass){
        Method[] declaredMethods = beanClass.getDeclaredMethods();
        List<MethodData> methodDataList=new ArrayList<>();
        if(ArrayUtil.isNotEmpty(declaredMethods)){
            for (Method declaredMethod : declaredMethods) {
                //检查当前方法是否被@ApiProviderMethod注解
                ApiProviderMethod apiProviderMethod = declaredMethod.getAnnotation(ApiProviderMethod.class);
                if(apiProviderMethod!=null){
                    if(!checkUniqueMethodParameter(declaredMethod)){
                        throw new ApiProviderMethodNotSupportException("目前只支持RPC方法的参数唯一或者没有参数!暂不支持多参数的RPC方法!");
                    }
                    //解析注解参数
                    String methodName = apiProviderMethod.methodName();
                    if(StrUtil.isBlank(methodName)){
                        methodName=declaredMethod.getName();
                    }
                    if(StrUtil.isBlank(apiProviderMethod.httpUri())){
                        throw new ApiProviderMethodNotSupportException("RPC方法必须提供能够通过HTTP请求访问的HTTP URI映射路径!");
                    }
                    //HTTP接口的映射路径
                    String httpUri =spliceHttpUri(clientProperties.getGroup(),apiProviderMethod.httpUri());
                    //是否鉴权
                    boolean auth = apiProviderMethod.isAuth();
                    //方法参数类型名称
                    String parameterTypeName = apiProviderMethod.parameterTypeName();
                    if(StrUtil.isBlank(parameterTypeName)){
                        if(declaredMethod.getParameterTypes().length==1){
                            parameterTypeName=declaredMethod.getParameterTypes()[0].getName();
                        }else {
                            parameterTypeName="";
                        }
                    }
                    //封装MethodData对象
                    MethodData methodData=new MethodData();
                    methodData.setMethodName(methodName);
                    methodData.setUri(httpUri);
                    methodData.setAuth(auth);
                    methodData.setParameterTypeName(parameterTypeName);
                    methodDataList.add(methodData);
                }
            }
        }
        return methodDataList;
    }

    /**
     * 检查当前方法的参数是否唯一
     * 要么就没有参数
     * 要么就只有一个参数
     * @param method
     * @return
     */
    private boolean checkUniqueMethodParameter(Method method){
        Class<?>[] parameterTypes = method.getParameterTypes();
        if(parameterTypes.length<2){
            return true;
        }
        return false;
    }

    private String spliceHttpUri(String group,String httUri){
        return group+HTTP_URI_SEPARATOR+httUri;
    }
}
